1. Descripció del dataset

Aquest dataset prové de la pràctica anterior, en la qual, no vaig prestar gens d’atenció a la neteja de les dades, donant com a resultat un dataset molt brut. Això es va fer a proposit per tal de poder aprofitar aquest dataset en aquesta pràctica.

Aquest dataset conté informació sobre ofertes laborals trobades a la web proporcionada per l’estat Espanyol per a tal proposit.

La pregunta que volem respondre amb aquest dataset serà:

A la pràctica anterior enumerabem també les següents idees:

Però aquestes les deixarem per futurs treballs.

2. Integració i selecció de les dades d’interès a analitzar

Per aquest apartat ja es va crear un script python que s’encarregaba d’ajuntar les dades obtingudes en diferents dies. La idea darrera d’aquest script era la de recolectar totes les dades disponibles a la web en una primera pasada, i desprès anar actualitzant el dataset agafant dades diaries i agrupant-les sota el mateix fitxer .csv

Per tant, en aquest apartat considero que no haig de fer més que el ja fet fins a la data.

El script es pot trobar en la següent URL: [https://github.com/amilan/spanish_job_market/blob/master/src/dataset_merge.py]

També tinc en compte, que la web oficial de la qual es va extreure les dades, ja recopila aquestes dades de diferent fonts, així doncs, no considero que sigui necessari l’integració de dades de diferentes fonts, ja que aquesta ha estat realitzada anteriorment.

Hauria d’explicar una mica com faria aquesta integració en cas de que no hagués estat feta per la propia font utilitzada? Revisar si s’ha d’expandir més aquesta explicació sobre la integració i selecció de dades d’interès

En aquest apartat seleccionarem les dades necessaries per als nostres estudis. Hem de tenir en compte, que a la pràctica anterior hem vaig limitar a agafar totes les dades possibles i a possar-les en un fitxer .csv. Aquestes dades provenien d’una base de dades NoSQL, ja que vaig detectar que amb les mateixes crides, podiem obtenir dades amb diferents esquemes (schemaless). Així doncs, farem una selecció de les dades que utilitzarem i eliminarem així dades no necessàries o repetides.

Començem carregant les dades:

offers <- read.csv("./data/offers_dataset.csv")
head(offers)
length(offers$categoria)
[1] 40534

Com podem veure, tenim 40534 registres i 94 característiques, moltes de les quals no ens seràn d’utilitat.

names(offers)
 [1] "categoria"           "categoriaF"          "categoriaS"          "ciudad"              "ciudadF"             "companiaSeleccion"   "competenciasReq"     "comunidad"           "comunidadF"         
[10] "consulta1"           "consulta2"           "consulta3"           "consulta4"           "contenido"           "creador"             "cuestionario"        "discapacidad"        "duracion"           
[19] "educacion"           "educacionDes"        "educacionDesF"       "educacionF"          "educacionReq"        "educacionReqF"       "educacionS"          "email"               "empresaSocial"      
[28] "ett"                 "fechaCreacion"       "fechaCreacionBoost"  "fechaCreacionPortal" "fechaIncorporacion"  "fechaRevision"       "formacionReq"        "horario"             "id"                 
[37] "jornada"             "jornadaF"            "localizacion"        "minExperiencia"      "nivel"               "noMeInteresa"        "numCandidatos"       "oReq"                "oferta"             
[46] "origen"              "pais"                "paisF"               "paisS"               "provincia"           "provinciaF"          "provinciaLimitrofe"  "provinciaS"          "publicado1"         
[55] "publicado2"          "publicado3"          "publicado4"          "respuesta1A"         "respuesta1B"         "respuesta2A"         "respuesta2B"         "respuesta2C"         "respuesta3A"        
[64] "respuesta3B"         "salarioMax"          "salarioMin"          "score"               "sector"              "sectorF"             "sisgarjuv"           "speState"            "speStateId"         
[73] "subcategoria"        "subcategoriaF"       "subcategoriaS"       "subsector"           "subsectorF"          "tamanoCompania2"     "telefono"            "tipoContrato"        "tipoContratoN"      
[82] "titulo"              "trabajosOfertados"   "url"                 "valor1A"             "valor1B"             "valor2A"             "valor2B"             "valor3B"             "verMail"            
[91] "verSalarioMax"       "verSalarioMin"       "verTelefono"         "web"                

Així doncs, començarem seleccionant les dades d’interés. Recordem que la meva intenció es la de fer un estudi sobre els tipus d’ofertes de treballs a Espanya i en concret a cadascuna de les regions.

Primerament, comprovarem que només tenim dades d’ofertes realitzades a Espanya.

levels(offers$paisS)
[1] "CONGO"   "ESPAÑA" "ESPAÑA"  "ESPA��A"

Comprovem dues coses, que tenim ofertes d’Espanya i també al Congo, i que tenim un problema de codificació de caracters, ja que ens troba el país d’Espanya en tres factors diferents. Com que només volem utilitzar les dades de les ofertes a Espanya, podem seleccionar totes les que no siguin al Congo i desprès eliminar aquesta columna.

Podem corregir les dades erronees de país:

offers$paisS <- sub("ESPAÑA", "ESPAÑA", offers$paisS)
offers$paisS <- sub("ESPA��A", "ESPAÑA", offers$paisS)
levels(factor(offers$paisS))
[1] "CONGO"  "ESPAÑA"

També podriem haver canviat la codificació dels caracters, com veurem més endavant.

Seleccionem ara només les ofertes a Espanya.

#offers_sp <- subset(offers, !(paisS %in% c("CONGO"))
#offers_sp <- subset(offers, !(paisS == "CONGO"))
offers <- subset(offers, paisS == "ESPAÑA")
levels(factor(offers$paisS))
[1] "ESPAÑA"
# paisS es ara del tipus chr, hauriem de convertirla de nou a factor
offers$paisS <- factor(offers$paisS)
class(offers$paisS)
[1] "factor"

Seguidament, eliminarem les colummnes que ofereixen informació duplicada. Ens quedarem amb les característiques:

selected_features <- c("categoriaF", "ciudadF", "comunidadF", "educacionF", "fechaCreacion", "jornadaF", "provinciaS", "salarioMax", "salarioMin", "subcategoriaS")
offers <- offers[selected_features]
head(offers)

3. Neteja de les dades

Com que ens hem adonat abans que hi teniem problemes de codificació amb els strings, lo primer que farem serà corregir aquests problemes.

levels(offers$comunidadF)
 [1] ""                       "ANDALUCÃ�A"             "ANDALUCÍA"              "ARAGÓN"                "ARAGÓN"                 "CANTABRIA"              "CASTILLA LA MANCHA"     "CASTILLA Y LEÓN"      
 [9] "CASTILLA Y LEÓN"        "CASTILLA Y LE��N"       "CATALUÑA"              "CATALUÑA"               "CATALU��A"              "CEUTA"                  "COMUNIDAD VALENCIANA"   "EXTREMADURA"           
[17] "GALICIA"                "ILLES BALEARS"          "ISLAS CANARIAS"         "LA RIOJA"               "MADRID"                 "MELILLA"                "NAVARRA"                "PA�S VASCO"           
[25] "PAÍS VASCO"             "PRINCIPADO DE ASTURIAS" "REGIÓN DE MURCIA"      "REGIÓN DE MURCIA"       "REGI��N DE MURCIA"      "Sin especificar"       
offers$comunidadF <- sub("ARAGÓN", "ARAGÓN", offers$comunidadF)
offers$comunidadF <- sub("CASTILLA Y LE��N", "CASTILLA Y LEÓN", offers$comunidadF)
offers$comunidadF <- sub("CASTILLA Y LEÓN", "CASTILLA Y LEÓN", offers$comunidadF)
offers$comunidadF <- sub("CATALU��A", "CATALUÑA", offers$comunidadF)
offers$comunidadF <- sub("CATALUÑA", "CATALUÑA", offers$comunidadF)
offers$comunidadF <- sub("ANDALUCÃ�A", "ANDALUCÍA", offers$comunidadF)
offers$comunidadF <- sub("REGI��N DE MURCIA", "REGIÓN DE MURCIA", offers$comunidadF)
offers$comunidadF <- sub("REGIÓN DE MURCIA", "REGIÓN DE MURCIA", offers$comunidadF)
offers$comunidadF <- sub("PAÃ�S VASCO", "PAÍS VASCO", offers$comunidadF)
offers$comunidadF <- sub("Sin especificar", "", offers$comunidadF)
offers$comunidadF <- factor(offers$comunidadF)
levels(offers$comunidadF)
 [1] ""                       "ANDALUCÍA"              "ARAGÓN"                 "CANTABRIA"              "CASTILLA LA MANCHA"     "CASTILLA Y LEÓN"        "CATALUÑA"               "CEUTA"                 
 [9] "COMUNIDAD VALENCIANA"   "EXTREMADURA"            "GALICIA"                "ILLES BALEARS"          "ISLAS CANARIAS"         "LA RIOJA"               "MADRID"                 "MELILLA"               
[17] "NAVARRA"                "PAÍS VASCO"             "PRINCIPADO DE ASTURIAS" "REGIÓN DE MURCIA"      

Veiem que en aquest cas podem tenir valor buit (“”) o sin especificar. Ens interessa deixar-ho com valor buit, ja que sabem que la oferta ha d’estar ubicada en alguna comunitat, però no sabem en quina.

levels(offers$categoriaF)
 [1] ""                                     "ADMINISTRACIÓN"                      "ADMINISTRACIÓN"                       "AGRICULTURA/JARDINERÍA/ALIMENTACIÓN"  "ALMACENES/REPONEDORES"               
 [6] "APRENDICES/PRIMER EMPLEO"             "ARQUITECTURA/DISEÑO"                  "COMERCIAL/VENTAS"                     "COMUNICACIÓN/CULTURA/ENTRETENIMIENTO" "CONDUCTORES/TRANSPORTE"              
[11] "CONSTRUCCIÓN"                         "CONSTRUCCI��N"                        "CUIDADOS/ASISTENCIA EN EL HOGAR"      "DEPENDIENTES/INFORMACIÓN"             "DERECHO/PSICOLOGÍA/CIENCIAS SOCIALES"
[16] "DIRECTIVOS"                           "EDUCACIÓN/SERVICIOS SOCIALES"        "EDUCACIÓN/SERVICIOS SOCIALES"         "EDUCACI��N/SERVICIOS SOCIALES"        "ELECTRICIDAD/ELECTRÓNICA/ENERGÍA"    
[21] "ELECTRICIDAD/ELECTR��NICA/ENERG��A"   "FÁBRICAS/INDUSTRIA"                   "FÃ�BRICAS/INDUSTRIA"                  "HOSTELERÍA/TURISMO"                   "HOSTELER��A/TURISMO"                 
[26] "INFORMÁTICA/TELECOMUNICACIONES"       "INFORMÃ�TICA/TELECOMUNICACIONES"      "INFORM��TICA/TELECOMUNICACIONES"      "INGENIERÃ�A/CALIDAD/CIENCIAS"         "INGENIERÍA/CALIDAD/CIENCIAS"         
[31] "METAL/MECÁNICA"                       "METAL/MECÃ�NICA"                      "PELUQUERÍA/ESTÉTICA"                  "SALUD/DEPORTE"                        "VIGILANCIA/SERVICIOS"                

En comptes de corregir un a un, transformarem les dades al format latin1.

# convertim les dades a encoding latin1
offers$categoriaF <- factor(iconv(offers$categoriaF, to = "latin1"))
levels(offers$categoriaF)
 [1] ""                                     "ADMINISTRACIÓN"                       "AGRICULTURA/JARDINERÍA/ALIMENTACIÓN"  "ALMACENES/REPONEDORES"                "APRENDICES/PRIMER EMPLEO"            
 [6] "ARQUITECTURA/DISEÑO"                  "COMERCIAL/VENTAS"                     "COMUNICACIÓN/CULTURA/ENTRETENIMIENTO" "CONDUCTORES/TRANSPORTE"               "CONSTRUCCIÓN"                        
[11] "CUIDADOS/ASISTENCIA EN EL HOGAR"      "DEPENDIENTES/INFORMACIÓN"             "DERECHO/PSICOLOGÍA/CIENCIAS SOCIALES" "DIRECTIVOS"                           "EDUCACIÓN/SERVICIOS SOCIALES"        
[16] "ELECTRICIDAD/ELECTRÓNICA/ENERGÍA"     "FÁBRICAS/INDUSTRIA"                   "HOSTELERÍA/TURISMO"                   "INFORMÁTICA/TELECOMUNICACIONES"       "INGENIERÍA/CALIDAD/CIENCIAS"         
[21] "METAL/MECÁNICA"                       "PELUQUERÍA/ESTÉTICA"                  "SALUD/DEPORTE"                        "VIGILANCIA/SERVICIOS"                

TODO: Revisar provincias!!! Guipuzcua esta repetida!!!!

levels(offers$provinciaS)
 [1] ""                       "ÁLAVA"                  "ALBACETE"               "ALICANTE"               "ALMERÍA"                "ASTURIAS"               "ÁVILA"                  "BADAJOZ"               
 [9] "BALEARS (ILLES)"        "BARCELONA"              "BURGOS"                 "CÁCERES"                "CÁDIZ"                  "CANTABRIA"              "CASTELLÓN"              "CEUTA"                 
[17] "CIUDAD REAL"            "CÓRDOBA"                "CORUÑA (A)"             "CUENCA"                 "GIRONA"                 "GRANADA"                "GUADALAJARA"            "GUIPÚZCOA"            
[25] "GUIPÚZCOA"              "HUELVA"                 "HUESCA"                 "JAÉN"                   "LEÓN"                   "LLEIDA"                 "LUGO"                   "MADRID"                
[33] "MÁLAGA"                 "MELILLA"                "MURCIA"                 "NAVARRA"                "OURENSE"                "PALENCIA"               "PALMAS (LAS)"           "PONTEVEDRA"            
[41] "RIOJA (LA)"             "SALAMANCA"              "SANTA CRUZ DE TENERIFE" "SEGOVIA"                "SEVILLA"                "SORIA"                  "TARRAGONA"              "TERUEL"                
[49] "TOLEDO"                 "VALENCIA"               "VALLADOLID"             "VIZCAYA"                "ZAMORA"                 "ZARAGOZA"              

Podem veure que Guipúzcua està repetida degut a la mala codificació.

# convertim les dades a encoding latin1
offers$provinciaS <- factor(iconv(offers$provinciaS, to = "latin1"))
levels(offers$provinciaS)
 [1] ""                       "ÁLAVA"                  "ALBACETE"               "ALICANTE"               "ALMERÍA"                "ASTURIAS"               "ÁVILA"                  "BADAJOZ"               
 [9] "BALEARS (ILLES)"        "BARCELONA"              "BURGOS"                 "CÁCERES"                "CÁDIZ"                  "CANTABRIA"              "CASTELLÓN"              "CEUTA"                 
[17] "CIUDAD REAL"            "CÓRDOBA"                "CORUÑA (A)"             "CUENCA"                 "GIRONA"                 "GRANADA"                "GUADALAJARA"            "GUIPÚZCOA"             
[25] "HUELVA"                 "HUESCA"                 "JAÉN"                   "LEÓN"                   "LLEIDA"                 "LUGO"                   "MADRID"                 "MÁLAGA"                
[33] "MELILLA"                "MURCIA"                 "NAVARRA"                "OURENSE"                "PALENCIA"               "PALMAS (LAS)"           "PONTEVEDRA"             "RIOJA (LA)"            
[41] "SALAMANCA"              "SANTA CRUZ DE TENERIFE" "SEGOVIA"                "SEVILLA"                "SORIA"                  "TARRAGONA"              "TERUEL"                 "TOLEDO"                
[49] "VALENCIA"               "VALLADOLID"             "VIZCAYA"                "ZAMORA"                 "ZARAGOZA"              
levels(offers$jornadaF)
[1] ""            "COMPLETA"    "INDIFERENTE" "PARCIAL"    
# convertim les dades a encoding latin1
offers$subcategoriaS <- factor(iconv(offers$subcategoriaS, to = "latin1"))
levels(offers$subcategoriaS)
  [1] ""                                         "ABOGADOS"                                 "ACUICULTURA"                              "ADMINISTRATIVOS"                         
  [5] "AGENCIA DE VIAJES"                        "AGENTES COMERCIALES/REPRESENTANTES"       "AGRICULTURA/GANADERÍA"                    "ALBAÑILERIA/ACABADOS"                    
  [9] "ANALISTAS/PROGRAMADORES"                  "ANIMACIÓN TURÍSTICA"                      "ANIMACIÓN/TIEMPO LIBRE"                   "APRENDICES"                              
 [13] "APUESTAS Y JUEGO"                         "AREA DIRECTIVA"                           "ARQUITECTOS"                              "ARTE/CULTURA/ESPECTÁCULOS"               
 [17] "ARTESANÍA/OFICIOS"                        "ASISTENCIA E INTEGRACIÓN SOCIAL"          "AUXILIARES DE ENFERMERÍA/GERIATRÍA"       "AUXILIARES DE SERVICIO/PERSONAL SUBALTER"
 [21] "AYUDA DOMICILIARIA"                       "AYUDANTES DE COCINA/COMIDA RÁPIDA"        "BANCA/SEGUROS"                            "BIBLIOTECAS/ARCHIVOS/MUSEOS"             
 [25] "BUCEADORES"                               "CAJEROS SUPERMERCADO"                     "CAJEROS Y TAQUILLEROS"                    "CALIDAD"                                 
 [29] "CAMAREROS"                                "CARNICERÍA/CHARCUTERIA"                   "CARPINTERÍA"                              "CERRAJEROS/FORJA/HERREROS"               
 [33] "COCINEROS"                                "COMERCIO EXTERIOR"                        "COMPRAS/DISTRIBRUCIÓN"                    "COMUNICACIÓN/PUBLICIDAD/MARKETING"       
 [37] "CONSERVAS/ALIMENTOS/BEBIDAS"              "CONTABILIDAD/FINANZAS"                    "CRISTALEROS"                              "CUIDADO DE ANCIANOS/NIÑOS"               
 [41] "CUIDADORES Y ADIESTRADORES DE ANIMALES"   "DECORADORES"                              "DELINEANTES/PROYECTISTAS"                 "DEPENDIENTES"                            
 [45] "DIETÉTICA Y NUTRICIÓN"                    "DISEÑO GRÁFICO/WEB"                       "DISEÑO INDUSTRIAL"                        "ECONOMISTAS"                             
 [49] "EDICIÓN/INDUSTRIA GRÁFICA"                "EDUCACIÓN ESPECIAL/PEDAGOGÍA"             "EDUCACIÓN INFANTIL"                       "ELECTRICIDAD"                            
 [53] "ELECTRÓNICA"                              "ENCARGADOS DE OBRAS/CAPATACES"            "ENCARGADOS Y SUPERVISORES DE PRODUCCIÓN"  "ENCOFRADO/FERRALLA/ESTRUCTURAS"          
 [57] "ENCUESTADORES"                            "ENERGÍAS RENOVABLES"                      "ENFERMERÍA"                               "ENSEÑANZA DE IDIOMAS"                    
 [61] "ENSEÑANZA PRIMARIA Y SECUNDARIA"          "ERP/CRM/BUSSINESS INTELLIGENCE"           "ESTÉTICA"                                 "FARMACIA/ÓPTICA/AUDICIÓN"                
 [65] "FISIOTERAPIA/DEPORTE/MANTENIMIENTO FISIC" "FONTANERÍA"                               "FORESTAL/JARDINERÍA"                      "FORMACIÓN PROFESIONAL"                   
 [69] "FRÍO Y CLIMATIZACIÓN"                     "FUNDICIÓN/SIDERURGÍA"                     "GESTIÓN DE ALMACENES"                     "GESTIÓN DE PROYECTOS"                    
 [73] "GRÚAS/EXCAVADORAS/MAQUINARIA"             "I+D"                                      "IMAGEN Y SONIDO/AUDIOVISUAL"              "INFORMACIÓN/AZAFATAS/PROMOCIÓN COMERCIAL"
 [77] "INGENIERÍAS"                              "LIMPIEZA"                                 "MADERA Y MUEBLE/EBANISTERÍA"              "MAQUINA HERRAMIENTA/TORNO/FRESA"         
 [81] "MECÁNICA DEL AUTOMÓVIL"                   "MECÁNICA/MANTENIMIENTO"                   "MEDICINA"                                 "MEDIO AMBIENTE/QUÍMICA/BIOLOGÍA/LABORATO"
 [85] "MENSAJERÍA"                               "MICROINFORMATICA/ASISTENCIA TÉCNICA"      "MINAS/CANTERAS/PERFORACIONES"             "MONTADORES Y ENSAMBLADORES"              
 [89] "MOZOS DE ALMACÉN/CARRETILLEROS"           "NAVEGACIÓN"                               "ORDENANZAS/CONSERJES"                     "OTRAS ACTIVIDADES"                       
 [93] "OTRAS ACTIVIDADES TÉCNICAS"               "OTROS PROFESIONALES"                      "PANADERÍA/REPOSTERÍA"                     "PELUQUERÍA"                              
 [97] "PEONES"                                   "PEONES/OPERARIOS/MANIPULADORES"           "PERIODISMO E INFORMACIÓN"                 "PESCA"                                   
[101] "PINTURA"                                  "POMPAS FUNEBRES"                          "PREVENCIÓN RIESGOS LABORALES"             "PRIMER EMPLEO"                           
[105] "PRODUCCIÓN Y DISTRIBUCCIÓN DE ENERGIA"    "PROFESORES DE ENSEÑANZA SUPERIOR"         "PSICÓLOGOS"                               "RECEPCIONISTAS"                          
[109] "RECEPCIONISTAS DE HOTEL"                  "RECURSOS HUMANOS/PERSONAL"                "REPARTO DE MERCANCÍAS"                    "REPONEDORES"                             
[113] "SALUD DENTAL"                             "SECRETARIADO"                             "SEGURIDAD"                                "SERVICIO DOMÉSTICO"                      
[117] "SERVICIOS VETERINARIOS"                   "SISTEMAS/SEGURIDAD/REDES"                 "SOCIÓLOGOS"                               "SOLDADURA/CALDERERÍA/CHAPA"              
[121] "TECNICOS EN CONSTRUCCION"                 "TECNICOS METALURGICOS"                    "TELECOMUNICACIONES"                       "TELEOPERADORES"                          
[125] "TEXTIL/CONFECCIÓN/CALZADO"                "TOPOGRAFÍA"                               "TRANSPORTE AÉREO"                         "TRANSPORTE DE PERSONAS O MERCANCIAS"     
[129] "TRANSPORTE INTERNACIONAL/ESPECIAL"        "VENDEDORES"                               "VIGILANCIA/GUARDAS JURADOS"              
# convertim les dades a encoding latin1
offers$educacionF <- factor(iconv(offers$educacionF, to = "latin1"))
levels(offers$educacionF)
 [1] ""                                "Bachillerato"                    "Certificados de Profesionalidad" "Diplomado o Ingeniero Técnico"  "Diplomado o Ingeniero Técnico"   "Doctor Universitario"           
 [7] "ESO, EGB, Graduado Escolar"      "Estudios primarios"              "FP I, Ciclo de Grado Medio"      "FP II, Ciclo de Grado Superior"  "Licenciado o Ingeniero Superior" "Otras Formaciones"              
[13] "Postgrado Universitario"         "Sin especificar"                 "Sin estudios"                   

Tot i la conversió, encara tenim algun cas que no s’ha codificat correctament. El corregirem manualment.

offers$educacionF <- sub("Diplomado o Ingeniero Técnico", "Diplomado o Ingeniero Técnico", offers$educacionF)
offers$educacionF <- sub("Sin especificar", "", offers$educacionF)
offers$educacionF <- factor(offers$educacionF)
levels(offers$educacionF)
 [1] ""                                "Bachillerato"                    "Certificados de Profesionalidad" "Diplomado o Ingeniero Técnico"   "Doctor Universitario"            "ESO, EGB, Graduado Escolar"     
 [7] "Estudios primarios"              "FP I, Ciclo de Grado Medio"      "FP II, Ciclo de Grado Superior"  "Licenciado o Ingeniero Superior" "Otras Formaciones"               "Postgrado Universitario"        
[13] "Sin estudios"                   
# convertim les dades a encoding latin1
offers$ciudadF <- factor(iconv(offers$ciudadF, to = "latin1"))
length(levels(offers$ciudadF))
[1] 1905

He detectat que encara hi ha algun cas en el que no s’ha corregit bé, com per exemple Gijón, que apareix dues vegades en diferent format, però en aquest estudi no anirem al detall de ciutat, així que de moment no corregiré aquest problema.

Les dades contenen zeros o elements buits? Com gestionaries aquests casos?

Com hem vist anteriorment, tenim dades amb element buits en les característiques del tipus factor. Hem convertit també les dades amb valors sin especificar en valor buit, ja que aquest, per exemple, representa millor que la oferta està situada a una localització, però no sabem a on.

Passem ara a mirar si tenim elements nulls (NA).

sapply(offers, function(x)(sum(is.na(x))))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
           23             0             0             0             0             0             1         38087         31667             8 
length(offers$salarioMax)
[1] 40533

Passem ara a netejar les característiques numériques. Veiem que aproximadament una quarta part de les dades dispossen de valors de salari mínim i máxim. Aquests ens podrien ser suficient per al nostres estudi, sempre i quan tinguem suficient casos d’estudi per a les diferentes regions.

Llavors, ens quedarem amb les dades que tenen un salari mínim i descartarem la resta.

offers_sp <- subset(offers, !is.na(offers$salarioMin))
sapply(offers_sp, function(x)(sum(is.na(x))))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
            4             0             0             0             0             0             0          6420             0             2 

Com que hi ha menys dades amb salari máxim, podriem seguir alguna de les següents estratègies: - Descartar les dades sense salari maxim. Això reduiría molt el nombre de dades, però encara tindriem prou per al estudi que volem realitzar. - Imputar dades utilitzant KNN. Amb aquesta estratègia podriem obtenir els valors en funció de les dades veines. - Imputar els valors en funció de la mitjana poblacional de la mostra.

En aquesta ocasió utilitzarem l’algoritme KNN.

offers_sp$salarioMax <- kNN(offers_sp)$salarioMax
sapply(offers_sp, function(x)(sum(is.na(x))))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
            4             0             0             0             0             0             0             0             0             2 

NOTE: the following chunk seems to be a mistake in executing the previous code several times. Veiem que tenim moltes ciutats amb NA. Seguirem la mateixa estrategia que abans i com que sabem que la oferta ha d’estar ubicada en alguna ciutat (encara que sigui oferta online), substituirem els NA per valors buits “”.

offers_sp$ciudadF[is.na(offers_sp$ciudadF)] <- ""
sapply(offers_sp, function(x)(sum(is.na(x))))

Ara ens queden per tractar 4 casos de categoriaF i 2 de subcategoriaS.

kable(subset(offers_sp, is.na(offers_sp$categoriaF)))
categoriaF ciudadF comunidadF educacionF fechaCreacion jornadaF provinciaS salarioMax salarioMin subcategoriaS
32704 NA Las Palmas de Gran Canaria ISLAS CANARIAS Otras Formaciones 2018-06-05T16:50:28Z PARCIAL PALMAS (LAS) 1000 600 NA
32705 NA Bilbao PAÍS VASCO Licenciado o Ingeniero Superior 2018-03-02T08:33:14Z COMPLETA VIZCAYA 25000 20000 NA
32706 NA Bilbao PAÍS VASCO FP II, Ciclo de Grado Superior 2018-03-02T08:27:33Z COMPLETA VIZCAYA 25000 20000 SISTEMAS/SEGURIDAD/REDES
32707 NA Bilbao PAÍS VASCO FP II, Ciclo de Grado Superior 2018-03-02T08:15:32Z COMPLETA VIZCAYA 25000 20000 ANALISTAS/PROGRAMADORES

Mirant la subcategoria, veiem clarament que les dues últimes pertanyen a la categoria: INFORMÁTICA/TELECOMUNICACIONES, però malauradament, les dues primeres no tenen subcategoria. Així doncs, descartarem les dues primeres i ens quedarem amb les dues últimes, introduint el nou valor a la categoria.

offers_sp <- subset(offers_sp, !is.na(offers_sp$subcategoriaS))
sapply(offers_sp, function(x)(sum(is.na(x))))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
            2             0             0             0             0             0             0             0             0             0 

Com que nomès ens queden dos valors NA per substituir i son els que coneixem, podem fer la següent operació.

offers_sp[is.na(offers_sp)] <- c("INFORMÁTICA/TELECOMUNICACIONES")
sapply(offers_sp, function(x)(sum(is.na(x))))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
            0             0             0             0             0             0             0             0             0             0 

Comprovem que ja no tenim cap valor NA.

summary(offers_sp)
                          categoriaF        ciudadF                    comunidadF                            educacionF                fechaCreacion         jornadaF        provinciaS     salarioMax     
 INFORMÁTICA/TELECOMUNICACIONES:1322            :1457   CATALUÑA            :3432                                 :4483   2018-09-20T10:10:36Z:  25              :1987   BARCELONA:2527   Min.   :      0  
 COMERCIAL/VENTAS              : 980   Barcelona:1037   MADRID              :1593   FP II, Ciclo de Grado Superior:1508   2018-10-23T15:23:57Z:  17   COMPLETA   :5402   MADRID   :1577   1st Qu.:   1200  
 ADMINISTRACIÓN                : 724   Madrid   : 968   ANDALUCÍA           :1205   ESO, EGB, Graduado Escolar    : 572   2018-10-26T15:08:28Z:  17   INDIFERENTE: 185   GIRONA   : 411   Median :   1600  
 SALUD/DEPORTE                 : 639   Girona   : 188   COMUNIDAD VALENCIANA: 511   FP I, Ciclo de Grado Medio    : 545   2018-11-01T00:08:47Z:  16   PARCIAL    :1290   VALENCIA : 284   Mean   :  11618  
 HOSTELERÍA/TURISMO            : 579   Valencia : 121   CASTILLA Y LEÓN     : 327   Diplomado o Ingeniero Técnico : 543   2018-11-03T00:08:37Z:  16                      TARRAGONA: 283   3rd Qu.:  18000  
 CONSTRUCCIÓN                  : 515   Sevilla  : 104   GALICIA             : 297   Estudios primarios            : 317   2018-10-26T14:04:15Z:  11                      SEVILLA  : 262   Max.   :9999999  
 (Other)                       :4105   (Other)  :4989   (Other)             :1499   (Other)                       : 896   (Other)             :8762                      (Other)  :3520                    
   salarioMin                                subcategoriaS 
 Min.   :     0   AGENTES COMERCIALES/REPRESENTANTES: 781  
 1st Qu.:  1000                                     : 554  
 Median :  1700   ANALISTAS/PROGRAMADORES           : 417  
 Mean   : 10091   ENFERMERÍA                        : 360  
 3rd Qu.: 18000   ADMINISTRATIVOS                   : 327  
 Max.   :105000   PEONES/OPERARIOS/MANIPULADORES    : 257  
                  (Other)                           :6168  

Seguidament podriem comprovar si les nostres dades tenen el tipus que desitjem.

sapply(offers_sp, function(x)(class(x)))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
     "factor"      "factor"      "factor"      "factor"      "factor"      "factor"      "factor"     "numeric"     "numeric"      "factor" 

Veiem que haurem de tractar el format de la característica fechaCreacion. En aquest moment, tenim la data com a un string amb el format: anys, mes, dia, hora. En el nostre cas, nomès amb l’any, mes i dia en tindrem prou. A més, haurem de donar-li el tipus de date type.

offers_sp$fechaCreacion <- as.Date(gsub("T\\d*:\\d*:\\d*Z", "", offers_sp$fechaCreacion))
sapply(offers_sp, function(x)(class(x)))
   categoriaF       ciudadF    comunidadF    educacionF fechaCreacion      jornadaF    provinciaS    salarioMax    salarioMin subcategoriaS 
     "factor"      "factor"      "factor"      "factor"        "Date"      "factor"      "factor"     "numeric"     "numeric"      "factor" 
summary(offers_sp)
                          categoriaF        ciudadF                    comunidadF                            educacionF   fechaCreacion               jornadaF        provinciaS     salarioMax        salarioMin    
 INFORMÁTICA/TELECOMUNICACIONES:1322            :1457   CATALUÑA            :3432                                 :4483   Min.   :2016-04-08              :1987   BARCELONA:2527   Min.   :      0   Min.   :     0  
 COMERCIAL/VENTAS              : 980   Barcelona:1037   MADRID              :1593   FP II, Ciclo de Grado Superior:1508   1st Qu.:2018-05-30   COMPLETA   :5402   MADRID   :1577   1st Qu.:   1200   1st Qu.:  1000  
 ADMINISTRACIÓN                : 724   Madrid   : 968   ANDALUCÍA           :1205   ESO, EGB, Graduado Escolar    : 572   Median :2018-09-04   INDIFERENTE: 185   GIRONA   : 411   Median :   1600   Median :  1700  
 SALUD/DEPORTE                 : 639   Girona   : 188   COMUNIDAD VALENCIANA: 511   FP I, Ciclo de Grado Medio    : 545   Mean   :2018-07-03   PARCIAL    :1290   VALENCIA : 284   Mean   :  11618   Mean   : 10091  
 HOSTELERÍA/TURISMO            : 579   Valencia : 121   CASTILLA Y LEÓN     : 327   Diplomado o Ingeniero Técnico : 543   3rd Qu.:2018-10-11                      TARRAGONA: 283   3rd Qu.:  18000   3rd Qu.: 18000  
 CONSTRUCCIÓN                  : 515   Sevilla  : 104   GALICIA             : 297   Estudios primarios            : 317   Max.   :2018-11-04                      SEVILLA  : 262   Max.   :9999999   Max.   :105000  
 (Other)                       :4105   (Other)  :4989   (Other)             :1499   (Other)                       : 896                                           (Other)  :3520                                     
                            subcategoriaS 
 AGENTES COMERCIALES/REPRESENTANTES: 781  
                                   : 554  
 ANALISTAS/PROGRAMADORES           : 417  
 ENFERMERÍA                        : 360  
 ADMINISTRATIVOS                   : 327  
 PEONES/OPERARIOS/MANIPULADORES    : 257  
 (Other)                           :6168  

Per últim, podem canviar el nombre de les característiques per que tinguin una mica més de sentit i guardem les dades en un nou fitxer csv.

names(offers_sp)
 [1] "categoriaF"    "ciudadF"       "comunidadF"    "educacionF"    "fechaCreacion" "jornadaF"      "provinciaS"    "salarioMax"    "salarioMin"    "subcategoriaS"
final_names <- c("Categoria", "Ciudad", "Comunidad", "Educacion", "FechaCreacion", "TipoJornada", "Provincia", "SalarioMax", "SalarioMin", "SubCategoria")
names(offers_sp) <- final_names
head(offers_sp)

Després de fer aquesta neteja, encara podriem comprobar si les nostres dades son consistents. Per fer aixó podriem mirar si tenim ofertes en les que el salari mínim sigui mallor que el salari màxim, i de ser així, eliminar-les del nostre dataset.

length(subset(offers_sp, SalarioMax < SalarioMin)$SalarioMax)
[1] 2035

Veiem que efectivament, tenim 2035 ofertes amb dades inconsistents. Procedirem doncs a eliminar-les.

offers_sp <- subset(offers_sp, SalarioMax >= SalarioMin)

Per últim, podriem exportar el nostre conjunt de dades netejat.

write.csv(offers_sp, "./data/spanish_job_offers_clean.csv")

Identificació i tractament de valors extrems.

Donem ara un cop d’ull a les dades per tal d’identificar valors extrems.

boxplot(offers_sp$SalarioMin)

boxplot(offers_sp$SalarioMax)
boxplot(offers_sp$SalarioMin ~ offers_sp$Comunidad)
boxplot(offers_sp$SalarioMax ~ offers_sp$Comunidad)
boxplot.stats(offers_sp$SalarioMin)$out
boxplot.stats(offers_sp$SalarioMax)$out

Veiem que tenim valors extrems tant en els salaris màxims com en els mínims. En el cas dels salaris mínims, son valor raonables, i crec que els hauriem de deixar tal qual son. En canvi, trobem dos valors extrems molt curiosos, que semblen ser alguna mena de valor prefixat per a no donar un límit superior. En aquest cas, ja que son només dos valors i tenim suficient dades per al nostre estudi, considero que lo millor sería treure les dades corresponents. Així doncs, ho farem de la següent manera.

kable(subset(offers_sp, SalarioMax == 9999999))
kable(subset(offers_sp, SalarioMin == 0))
offers_sp <- subset(offers_sp, !(SalarioMax==9999999))
offers_sp$SalarioMin <- as.numeric(offers_sp$SalarioMin)
offers_sp$SalarioMax <- as.numeric(offers_sp$SalarioMax)
boxplot(offers_sp$SalarioMax ~ offers_sp$Comunidad)

Veiem que els valors extrems que tenim ara son més raonables, i considero que els podriem deixar tal qual.

boxplot(offers_sp$SalarioMax ~ offers_sp$Categoria)

4. Anàlisi de les dades

Selecció dels grups de dades que es volen analitzar/comparar (planificació dels anàlisis a aplicar)

TODO: Agrupar por comunidad

Comprovació de la normalitat i homogeneïtat de la variància.

Començem mirant si les variables pertanyen a una distribució normal.

p_val_sal_min <- shapiro.test(subset(offers_sp, Comunidad == c("MADRID"))$SalarioMin)$p.value
#p_val_sal_max <- shapiro.test(offers_sp$SalarioMax)$p.value
sprintf("P value para SalarioMin: %f", p_val_sal_min)
#sprintf("P Value para SalarioMax: %d", p_val_sal_max)
hist(subset(offers_sp, Comunidad == c("MADRID"))$SalarioMin)
hist(subset(offers_sp, Comunidad == c("CATALUÑA"))$SalarioMin)

Aplicació de proves estadístiques per comparar els grups de dades. En funció de les dades de l’objectiu de l’estudi, aplicar proves de contrast d’hipòtesi, correlacions, regressions, etc.

cor_matrix <- cor(offers_sp$SalarioMin, offers_sp$SalarioMax)
round(cor_matrix, 2)

En aquest punt, ens adonem que hi ha un tipus de registres en els quals tenim 0 a salari minim i máxim, lo que vol dir que aquestes ofertes no han introduit un valor real en quant als salaris, o bé son ofertes de pràctiques no remunerades. Ninguna d’aquestes opcions les volem contemplar en el nostre estudi, així que com tenim dades suficients, podem prescindir d’aquestes.

offers_sp <- subset(offers_sp, !(SalarioMin == 0 & SalarioMax == 0))

TODO: En comptes d’utilitzar KNN utilitzar les mitjanes poblacionals per categoria TODO: Hi ha valors de salari Min i Max que s’han d’intercanviar.

5. Representació dels resultats a partir de taules i gràfiques.

6. Resolució del problema. A partir dels resultats obtinguts, quines són les conclusions? Els resultats permeten respondre al problema?

7. Codi: Cal adjuntar el codi

LS0tCnRpdGxlOiAiQ2xlYW5pbmcgU3BhbmlzaCBKb2IgTWFya2V0IERhdGFzZXQiCmF1dGhvcjogIkFudG9uaW8gTWlsw6FuIE90ZXJvIgpkYXRlOiAiRGVjZW1iZXIgMTUsIDIwMTgiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCmBgYHtyIGxvYWRfbGlicmFyaWVzLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShWSU0pCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShwc3ljaCkKbGlicmFyeShnZ3Bsb3QyKQojIGZvciBwbG90aW5nIGNvcnJlbGF0aW9ucwpsaWJyYXJ5KGVsbGlwc2UpCmBgYAoKIyMgMS4gRGVzY3JpcGNpw7MgZGVsIGRhdGFzZXQKCkFxdWVzdCBkYXRhc2V0IHByb3bDqSBkZSBsYSBwcsOgY3RpY2EgYW50ZXJpb3IsIGVuIGxhIHF1YWwsIG5vIHZhaWcgcHJlc3RhciBnZW5zIGQnYXRlbmNpw7MgYSBsYSBuZXRlamEgZGUgbGVzIGRhZGVzLCBkb25hbnQgY29tIGEgcmVzdWx0YXQgdW4gZGF0YXNldCBtb2x0IGJydXQuIEFpeMOyIGVzIHZhIGZlciBhIHByb3Bvc2l0IHBlciB0YWwgZGUgcG9kZXIgYXByb2ZpdGFyIGFxdWVzdCBkYXRhc2V0IGVuIGFxdWVzdGEgcHLDoGN0aWNhLgoKQXF1ZXN0IGRhdGFzZXQgY29udMOpIGluZm9ybWFjacOzIHNvYnJlIG9mZXJ0ZXMgbGFib3JhbHMgdHJvYmFkZXMgYSBsYSB3ZWIgcHJvcG9yY2lvbmFkYSBwZXIgbCdlc3RhdCBFc3BhbnlvbCBwZXIgYSB0YWwgcHJvcG9zaXQuCgpMYSBwcmVndW50YSBxdWUgdm9sZW0gcmVzcG9uZHJlIGFtYiBhcXVlc3QgZGF0YXNldCBzZXLDoDoKCi0gUXVpbmVzIHJlZ2lvbnMgZCdFc3BhbnlhIGdlbmVyZW4gbcOpcyBvZmVydGVzIGRlIHRyZWJhbGw/Ci0gUXVpbiB0aXB1cyBkZSBwcm9mZXNzaW9uYWwgZXMgZWwgbcOpcyBzb2xpY2l0YXQgYSBFc3BhbnlhIChkdXJhbnQgZWwgcGVyaW9kZSBkZSBtb3N0cmVpZyk/Ci0gRXN0dWRpIHNvYnJlIGVscyBzYWxhcmlzIGVuIHJlbGFjacOzIGEgbGVzIHJlZ2lvbnMuIEEgb24gdHJvYmVtIHVuIG1ham9yIHNhbGFyaT8KCkEgbGEgcHLDoGN0aWNhIGFudGVyaW9yIGVudW1lcmFiZW0gdGFtYsOpIGxlcyBzZWfDvGVudHMgaWRlZXM6CgotIEFuYWxpdHphciBlbHMgZGlmZXJlbnRzIHJlcXVlcmltZW50cyBwcm9mZXNzaW9uYWxzIHF1ZSB0ZW5lbiBsZXMgZGlmZXJlbnRzIGF1dG9ub21pZXMgZCdFc3BhbnlhLgotIElkZW50aWZpY2FyIGVsIHRpcHVzIGkgbGEgcXVhbGl0YXQgZGVsIHRyZWJhbGwgYWN0dWFsIGFsIHBhw61zLgotIEFuYWxpdHphciBsZXMgcmVnaW9ucyBhbWIgbcOpcyBpIG1lbnlzIG9mZXJ0ZXMgZGUgdHJlYmFsbC4KLSBBbmFsaXR6YXIgbGEgZGlzdHJpYnVjacOzIGRlIGxlcyBkaWZlcmVudHMgcHJvZmVzc2lvbnMgZW4gZnVuY2nDsyBkZSBsYSByZWdpw7MuCi0gQWp1ZGFyIGEgbGEgY3JlYWNpw7MgZOKAmXVuIHBsYSBwZXIgcG90ZW5jaWFyIGVsIG1lcmNhdCBsYWJvcmFsIGJhc2F0IGVuIGVsIGNvbmVpeGVtZW50IG9idGluZ3V0IGEgdHJhdsOpcyBkZSBsZXMgZGFkZXMuCgpQZXLDsiBhcXVlc3RlcyBsZXMgZGVpeGFyZW0gcGVyIGZ1dHVycyB0cmViYWxscy4KCiMjIDIuIEludGVncmFjacOzIGkgc2VsZWNjacOzIGRlIGxlcyBkYWRlcyBkJ2ludGVyw6hzIGEgYW5hbGl0emFyCgpQZXIgYXF1ZXN0IGFwYXJ0YXQgamEgZXMgdmEgY3JlYXIgdW4gc2NyaXB0IHB5dGhvbiBxdWUgcydlbmNhcnJlZ2FiYSBkJ2FqdW50YXIgbGVzIGRhZGVzIG9idGluZ3VkZXMgZW4gZGlmZXJlbnRzIGRpZXMuIExhIGlkZWEgZGFycmVyYSBkJ2FxdWVzdCBzY3JpcHQgZXJhIGxhIGRlIHJlY29sZWN0YXIgdG90ZXMgbGVzIGRhZGVzIGRpc3BvbmlibGVzIGEgbGEgd2ViIGVuIHVuYSBwcmltZXJhIHBhc2FkYSwgaSBkZXNwcsOocyBhbmFyIGFjdHVhbGl0emFudCBlbCBkYXRhc2V0IGFnYWZhbnQgZGFkZXMgZGlhcmllcyBpIGFncnVwYW50LWxlcyBzb3RhIGVsIG1hdGVpeCBmaXR4ZXIgLmNzdgoKUGVyIHRhbnQsIGVuIGFxdWVzdCBhcGFydGF0IGNvbnNpZGVybyBxdWUgbm8gaGFpZyBkZSBmZXIgbcOpcyBxdWUgZWwgamEgZmV0IGZpbnMgYSBsYSBkYXRhLgoKRWwgc2NyaXB0IGVzIHBvdCB0cm9iYXIgZW4gbGEgc2Vnw7xlbnQgVVJMOgpbaHR0cHM6Ly9naXRodWIuY29tL2FtaWxhbi9zcGFuaXNoX2pvYl9tYXJrZXQvYmxvYi9tYXN0ZXIvc3JjL2RhdGFzZXRfbWVyZ2UucHldCgpUYW1iw6kgdGluYyBlbiBjb21wdGUsIHF1ZSBsYSB3ZWIgb2ZpY2lhbCBkZSBsYSBxdWFsIGVzIHZhIGV4dHJldXJlIGxlcyBkYWRlcywgamEgcmVjb3BpbGEgYXF1ZXN0ZXMgZGFkZXMgZGUgZGlmZXJlbnQgZm9udHMsIGFpeMOtIGRvbmNzLCBubyBjb25zaWRlcm8gcXVlIHNpZ3VpIG5lY2Vzc2FyaSBsJ2ludGVncmFjacOzIGRlIGRhZGVzIGRlIGRpZmVyZW50ZXMgZm9udHMsIGphIHF1ZSBhcXVlc3RhIGhhIGVzdGF0IHJlYWxpdHphZGEgYW50ZXJpb3JtZW50LgoKPiBIYXVyaWEgZCdleHBsaWNhciB1bmEgbWljYSBjb20gZmFyaWEgYXF1ZXN0YSBpbnRlZ3JhY2nDsyBlbiBjYXMgZGUgcXVlIG5vIGhhZ3XDqXMgZXN0YXQgZmV0YSBwZXIgbGEgcHJvcGlhIGZvbnQgdXRpbGl0emFkYT8KPiBSZXZpc2FyIHNpIHMnaGEgZCdleHBhbmRpciBtw6lzIGFxdWVzdGEgZXhwbGljYWNpw7Mgc29icmUgbGEgaW50ZWdyYWNpw7MgaSBzZWxlY2Npw7MgZGUgZGFkZXMgZCdpbnRlcsOocwoKRW4gYXF1ZXN0IGFwYXJ0YXQgc2VsZWNjaW9uYXJlbSBsZXMgZGFkZXMgbmVjZXNzYXJpZXMgcGVyIGFscyBub3N0cmVzIGVzdHVkaXMuCkhlbSBkZSB0ZW5pciBlbiBjb21wdGUsIHF1ZSBhIGxhIHByw6BjdGljYSBhbnRlcmlvciBoZW0gdmFpZyBsaW1pdGFyIGEgYWdhZmFyIHRvdGVzIGxlcyBkYWRlcyBwb3NzaWJsZXMgaSBhIHBvc3Nhci1sZXMgZW4gdW4gZml0eGVyIC5jc3YuCkFxdWVzdGVzIGRhZGVzIHByb3ZlbmllbiBkJ3VuYSBiYXNlIGRlIGRhZGVzIE5vU1FMLCBqYSBxdWUgdmFpZyBkZXRlY3RhciBxdWUgYW1iIGxlcyBtYXRlaXhlcyBjcmlkZXMsIHBvZGllbSBvYnRlbmlyIGRhZGVzIGFtYiBkaWZlcmVudHMgZXNxdWVtZXMgKHNjaGVtYWxlc3MpLiBBaXjDrSBkb25jcywgZmFyZW0gdW5hIHNlbGVjY2nDsyBkZSBsZXMgZGFkZXMgcXVlIHV0aWxpdHphcmVtIGkgZWxpbWluYXJlbSBhaXjDrSBkYWRlcyBubyBuZWNlc3PDoHJpZXMgbyByZXBldGlkZXMuCgpDb21lbsOnZW0gY2FycmVnYW50IGxlcyBkYWRlczoKCmBgYHtyfQpvZmZlcnMgPC0gcmVhZC5jc3YoIi4vZGF0YS9vZmZlcnNfZGF0YXNldC5jc3YiKQpoZWFkKG9mZmVycykKYGBgCgpgYGB7cn0KbGVuZ3RoKG9mZmVycyRjYXRlZ29yaWEpCmBgYAoKQ29tIHBvZGVtIHZldXJlLCB0ZW5pbSA0MDUzNCByZWdpc3RyZXMgaSA5NCBjYXJhY3RlcsOtc3RpcXVlcywgbW9sdGVzIGRlIGxlcyBxdWFscyBubyBlbnMgc2Vyw6BuIGQndXRpbGl0YXQuCgpgYGB7cn0KbmFtZXMob2ZmZXJzKQpgYGAKCkFpeMOtIGRvbmNzLCBjb21lbsOnYXJlbSBzZWxlY2Npb25hbnQgbGVzIGRhZGVzIGQnaW50ZXLDqXMuIFJlY29yZGVtIHF1ZSBsYSBtZXZhIGludGVuY2nDsyBlcyBsYSBkZSBmZXIgdW4gZXN0dWRpIHNvYnJlIGVscyB0aXB1cyBkJ29mZXJ0ZXMgZGUgdHJlYmFsbHMgYSBFc3BhbnlhIGkgZW4gY29uY3JldCBhIGNhZGFzY3VuYSBkZSBsZXMgcmVnaW9ucy4KClByaW1lcmFtZW50LCBjb21wcm92YXJlbSBxdWUgbm9tw6lzIHRlbmltIGRhZGVzIGQnb2ZlcnRlcyByZWFsaXR6YWRlcyBhIEVzcGFueWEuCgpgYGB7cn0KbGV2ZWxzKG9mZmVycyRwYWlzUykKYGBgCgpDb21wcm92ZW0gZHVlcyBjb3NlcywgcXVlIHRlbmltIG9mZXJ0ZXMgZCdFc3BhbnlhIGkgdGFtYsOpIGFsIENvbmdvLCBpIHF1ZSB0ZW5pbSB1biBwcm9ibGVtYSBkZSBjb2RpZmljYWNpw7MgZGUgY2FyYWN0ZXJzLCBqYSBxdWUgZW5zIHRyb2JhIGVsIHBhw61zIGQnRXNwYW55YSBlbiB0cmVzIGZhY3RvcnMgZGlmZXJlbnRzLiBDb20gcXVlIG5vbcOpcyB2b2xlbSB1dGlsaXR6YXIgbGVzIGRhZGVzIGRlIGxlcyBvZmVydGVzIGEgRXNwYW55YSwgcG9kZW0gc2VsZWNjaW9uYXIgdG90ZXMgbGVzIHF1ZSBubyBzaWd1aW4gYWwgQ29uZ28gaSBkZXNwcsOocyBlbGltaW5hciBhcXVlc3RhIGNvbHVtbmEuCgpQb2RlbSBjb3JyZWdpciBsZXMgZGFkZXMgZXJyb25lZXMgZGUgcGHDrXM6CgpgYGB7cn0Kb2ZmZXJzJHBhaXNTIDwtIHN1YigiRVNQQcOD4oCYQSIsICJFU1BBw5FBIiwgb2ZmZXJzJHBhaXNTKQpvZmZlcnMkcGFpc1MgPC0gc3ViKCJFU1BB77+977+9QSIsICJFU1BBw5FBIiwgb2ZmZXJzJHBhaXNTKQpsZXZlbHMoZmFjdG9yKG9mZmVycyRwYWlzUykpCmBgYAoKVGFtYsOpIHBvZHJpZW0gaGF2ZXIgY2FudmlhdCBsYSBjb2RpZmljYWNpw7MgZGVscyBjYXJhY3RlcnMsIGNvbSB2ZXVyZW0gbcOpcyBlbmRhdmFudC4KClNlbGVjY2lvbmVtIGFyYSBub23DqXMgbGVzIG9mZXJ0ZXMgYSBFc3BhbnlhLgoKYGBge3J9CiNvZmZlcnNfc3AgPC0gc3Vic2V0KG9mZmVycywgIShwYWlzUyAlaW4lIGMoIkNPTkdPIikpCiNvZmZlcnNfc3AgPC0gc3Vic2V0KG9mZmVycywgIShwYWlzUyA9PSAiQ09OR08iKSkKb2ZmZXJzIDwtIHN1YnNldChvZmZlcnMsIHBhaXNTID09ICJFU1BBw5FBIikKbGV2ZWxzKGZhY3RvcihvZmZlcnMkcGFpc1MpKQojIHBhaXNTIGVzIGFyYSBkZWwgdGlwdXMgY2hyLCBoYXVyaWVtIGRlIGNvbnZlcnRpcmxhIGRlIG5vdSBhIGZhY3RvcgpvZmZlcnMkcGFpc1MgPC0gZmFjdG9yKG9mZmVycyRwYWlzUykKYGBgCmBgYHtyfQpjbGFzcyhvZmZlcnMkcGFpc1MpCmBgYAoKU2VndWlkYW1lbnQsIGVsaW1pbmFyZW0gbGVzIGNvbHVtbW5lcyBxdWUgb2ZlcmVpeGVuIGluZm9ybWFjacOzIGR1cGxpY2FkYS4gRW5zIHF1ZWRhcmVtIGFtYiBsZXMgY2FyYWN0ZXLDrXN0aXF1ZXM6CgotIGNhdGVnb3JpYUYKLSBjaXVkYWRGCi0gY29tdW5pZGFkRgotIGVkdWNhY2lvbkYKLSBmZWNoYUNyZWFjaW9uCi0gam9ybmFkYUYKLSBwcm92aW5jaWFTCi0gc2FsYXJpb01heAotIHNhbGFyaW9NaW4KLSBzdWJjYXRlZ29yaWFTCgpgYGB7cn0Kc2VsZWN0ZWRfZmVhdHVyZXMgPC0gYygiY2F0ZWdvcmlhRiIsICJjaXVkYWRGIiwgImNvbXVuaWRhZEYiLCAiZWR1Y2FjaW9uRiIsICJmZWNoYUNyZWFjaW9uIiwgImpvcm5hZGFGIiwgInByb3ZpbmNpYVMiLCAic2FsYXJpb01heCIsICJzYWxhcmlvTWluIiwgInN1YmNhdGVnb3JpYVMiKQpvZmZlcnMgPC0gb2ZmZXJzW3NlbGVjdGVkX2ZlYXR1cmVzXQpoZWFkKG9mZmVycykKYGBgCgojIDMuIE5ldGVqYSBkZSBsZXMgZGFkZXMKCkNvbSBxdWUgZW5zIGhlbSBhZG9uYXQgYWJhbnMgcXVlIGhpIHRlbmllbSBwcm9ibGVtZXMgZGUgY29kaWZpY2FjacOzIGFtYiBlbHMgc3RyaW5ncywgbG8gcHJpbWVyIHF1ZSBmYXJlbSBzZXLDoCBjb3JyZWdpciBhcXVlc3RzIHByb2JsZW1lcy4KCgpgYGB7cn0KbGV2ZWxzKG9mZmVycyRjb211bmlkYWRGKQpgYGAKCmBgYHtyfQpvZmZlcnMkY29tdW5pZGFkRiA8LSBzdWIoIkFSQUfDg+KAnE4iLCAiQVJBR8OTTiIsIG9mZmVycyRjb211bmlkYWRGKQpvZmZlcnMkY29tdW5pZGFkRiA8LSBzdWIoIkNBU1RJTExBIFkgTEXvv73vv71OIiwgIkNBU1RJTExBIFkgTEXDk04iLCBvZmZlcnMkY29tdW5pZGFkRikKb2ZmZXJzJGNvbXVuaWRhZEYgPC0gc3ViKCJDQVNUSUxMQSBZIExFw4PigJxOIiwgIkNBU1RJTExBIFkgTEXDk04iLCBvZmZlcnMkY29tdW5pZGFkRikKb2ZmZXJzJGNvbXVuaWRhZEYgPC0gc3ViKCJDQVRBTFXvv73vv71BIiwgIkNBVEFMVcORQSIsIG9mZmVycyRjb211bmlkYWRGKQpvZmZlcnMkY29tdW5pZGFkRiA8LSBzdWIoIkNBVEFMVcOD4oCYQSIsICJDQVRBTFXDkUEiLCBvZmZlcnMkY29tdW5pZGFkRikKb2ZmZXJzJGNvbXVuaWRhZEYgPC0gc3ViKCJBTkRBTFVDw4Pvv71BIiwgIkFOREFMVUPDjUEiLCBvZmZlcnMkY29tdW5pZGFkRikKb2ZmZXJzJGNvbXVuaWRhZEYgPC0gc3ViKCJSRUdJ77+977+9TiBERSBNVVJDSUEiLCAiUkVHScOTTiBERSBNVVJDSUEiLCBvZmZlcnMkY29tdW5pZGFkRikKb2ZmZXJzJGNvbXVuaWRhZEYgPC0gc3ViKCJSRUdJw4PigJxOIERFIE1VUkNJQSIsICJSRUdJw5NOIERFIE1VUkNJQSIsIG9mZmVycyRjb211bmlkYWRGKQpvZmZlcnMkY29tdW5pZGFkRiA8LSBzdWIoIlBBw4Pvv71TIFZBU0NPIiwgIlBBw41TIFZBU0NPIiwgb2ZmZXJzJGNvbXVuaWRhZEYpCm9mZmVycyRjb211bmlkYWRGIDwtIHN1YigiU2luIGVzcGVjaWZpY2FyIiwgIiIsIG9mZmVycyRjb211bmlkYWRGKQpvZmZlcnMkY29tdW5pZGFkRiA8LSBmYWN0b3Iob2ZmZXJzJGNvbXVuaWRhZEYpCmxldmVscyhvZmZlcnMkY29tdW5pZGFkRikKYGBgCgpWZWllbSBxdWUgZW4gYXF1ZXN0IGNhcyBwb2RlbSB0ZW5pciB2YWxvciBidWl0ICgiIikgbyBfX3NpbiBlc3BlY2lmaWNhcl9fLiBFbnMgaW50ZXJlc3NhIGRlaXhhci1obyBjb20gdmFsb3IgYnVpdCwgamEgcXVlIHNhYmVtIHF1ZSBsYSBvZmVydGEgaGEgZCdlc3RhciB1YmljYWRhIGVuIGFsZ3VuYSBjb211bml0YXQsIHBlcsOyIG5vIHNhYmVtIGVuIHF1aW5hLgoKYGBge3J9CmxldmVscyhvZmZlcnMkY2F0ZWdvcmlhRikKYGBgCgpFbiBjb21wdGVzIGRlIGNvcnJlZ2lyIHVuIGEgdW4sIHRyYW5zZm9ybWFyZW0gbGVzIGRhZGVzIGFsIGZvcm1hdCBsYXRpbjEuCgpgYGB7cn0KIyBjb252ZXJ0aW0gbGVzIGRhZGVzIGEgZW5jb2RpbmcgbGF0aW4xCm9mZmVycyRjYXRlZ29yaWFGIDwtIGZhY3RvcihpY29udihvZmZlcnMkY2F0ZWdvcmlhRiwgdG8gPSAibGF0aW4xIikpCmxldmVscyhvZmZlcnMkY2F0ZWdvcmlhRikKYGBgCj4gVE9ETzoKUmV2aXNhciBwcm92aW5jaWFzISEhIEd1aXB1emN1YSBlc3RhIHJlcGV0aWRhISEhIQoKYGBge3J9CmxldmVscyhvZmZlcnMkcHJvdmluY2lhUykKYGBgCgpQb2RlbSB2ZXVyZSBxdWUgR3VpcMO6emN1YSBlc3TDoCByZXBldGlkYSBkZWd1dCBhIGxhIG1hbGEgY29kaWZpY2FjacOzLgoKYGBge3J9CiMgY29udmVydGltIGxlcyBkYWRlcyBhIGVuY29kaW5nIGxhdGluMQpvZmZlcnMkcHJvdmluY2lhUyA8LSBmYWN0b3IoaWNvbnYob2ZmZXJzJHByb3ZpbmNpYVMsIHRvID0gImxhdGluMSIpKQpsZXZlbHMob2ZmZXJzJHByb3ZpbmNpYVMpCmBgYAoKYGBge3J9CmxldmVscyhvZmZlcnMkam9ybmFkYUYpCmBgYAoKYGBge3J9CiMgY29udmVydGltIGxlcyBkYWRlcyBhIGVuY29kaW5nIGxhdGluMQpvZmZlcnMkc3ViY2F0ZWdvcmlhUyA8LSBmYWN0b3IoaWNvbnYob2ZmZXJzJHN1YmNhdGVnb3JpYVMsIHRvID0gImxhdGluMSIpKQpsZXZlbHMob2ZmZXJzJHN1YmNhdGVnb3JpYVMpCmBgYAoKYGBge3J9CiMgY29udmVydGltIGxlcyBkYWRlcyBhIGVuY29kaW5nIGxhdGluMQpvZmZlcnMkZWR1Y2FjaW9uRiA8LSBmYWN0b3IoaWNvbnYob2ZmZXJzJGVkdWNhY2lvbkYsIHRvID0gImxhdGluMSIpKQpsZXZlbHMob2ZmZXJzJGVkdWNhY2lvbkYpCmBgYAoKVG90IGkgbGEgY29udmVyc2nDsywgZW5jYXJhIHRlbmltIGFsZ3VuIGNhcyBxdWUgbm8gcydoYSBjb2RpZmljYXQgY29ycmVjdGFtZW50LiBFbCBjb3JyZWdpcmVtIG1hbnVhbG1lbnQuCgpgYGB7cn0Kb2ZmZXJzJGVkdWNhY2lvbkYgPC0gc3ViKCJEaXBsb21hZG8gbyBJbmdlbmllcm8gVMODwqljbmljbyIsICJEaXBsb21hZG8gbyBJbmdlbmllcm8gVMOpY25pY28iLCBvZmZlcnMkZWR1Y2FjaW9uRikKb2ZmZXJzJGVkdWNhY2lvbkYgPC0gc3ViKCJTaW4gZXNwZWNpZmljYXIiLCAiIiwgb2ZmZXJzJGVkdWNhY2lvbkYpCm9mZmVycyRlZHVjYWNpb25GIDwtIGZhY3RvcihvZmZlcnMkZWR1Y2FjaW9uRikKbGV2ZWxzKG9mZmVycyRlZHVjYWNpb25GKQpgYGAKCmBgYHtyfQojIGNvbnZlcnRpbSBsZXMgZGFkZXMgYSBlbmNvZGluZyBsYXRpbjEKb2ZmZXJzJGNpdWRhZEYgPC0gZmFjdG9yKGljb252KG9mZmVycyRjaXVkYWRGLCB0byA9ICJsYXRpbjEiKSkKbGVuZ3RoKGxldmVscyhvZmZlcnMkY2l1ZGFkRikpCmBgYAoKSGUgZGV0ZWN0YXQgcXVlIGVuY2FyYSBoaSBoYSBhbGd1biBjYXMgZW4gZWwgcXVlIG5vIHMnaGEgY29ycmVnaXQgYsOpLCBjb20gcGVyIGV4ZW1wbGUgR2lqw7NuLCBxdWUgYXBhcmVpeCBkdWVzIHZlZ2FkZXMgZW4gZGlmZXJlbnQgZm9ybWF0LCBwZXLDsiBlbiBhcXVlc3QgZXN0dWRpIG5vIGFuaXJlbSBhbCBkZXRhbGwgZGUgY2l1dGF0LCBhaXjDrSBxdWUgZGUgbW9tZW50IG5vIGNvcnJlZ2lyw6kgYXF1ZXN0IHByb2JsZW1hLgoKIyMgTGVzIGRhZGVzIGNvbnRlbmVuIHplcm9zIG8gZWxlbWVudHMgYnVpdHM/IENvbSBnZXN0aW9uYXJpZXMgYXF1ZXN0cyBjYXNvcz8KCkNvbSBoZW0gdmlzdCBhbnRlcmlvcm1lbnQsIHRlbmltIGRhZGVzIGFtYiBlbGVtZW50IGJ1aXRzIGVuIGxlcyBjYXJhY3RlcsOtc3RpcXVlcyBkZWwgdGlwdXMgZmFjdG9yLiBIZW0gY29udmVydGl0IHRhbWLDqSBsZXMgZGFkZXMgYW1iIHZhbG9ycyBfX3NpbiBlc3BlY2lmaWNhcl9fIGVuIHZhbG9yIGJ1aXQsIGphIHF1ZSBhcXVlc3QsIHBlciBleGVtcGxlLCByZXByZXNlbnRhIG1pbGxvciBxdWUgbGEgb2ZlcnRhIGVzdMOgIHNpdHVhZGEgYSB1bmEgbG9jYWxpdHphY2nDsywgcGVyw7Igbm8gc2FiZW0gYSBvbi4KClBhc3NlbSBhcmEgYSBtaXJhciBzaSB0ZW5pbSBlbGVtZW50cyBudWxscyAoTkEpLgoKYGBge3J9CnNhcHBseShvZmZlcnMsIGZ1bmN0aW9uKHgpKHN1bShpcy5uYSh4KSkpKQpgYGAKCmBgYHtyfQpsZW5ndGgob2ZmZXJzJHNhbGFyaW9NYXgpCmBgYAoKUGFzc2VtIGFyYSBhIG5ldGVqYXIgbGVzIGNhcmFjdGVyw61zdGlxdWVzIG51bcOpcmlxdWVzLiBWZWllbSBxdWUgYXByb3hpbWFkYW1lbnQgdW5hIHF1YXJ0YSBwYXJ0IGRlIGxlcyBkYWRlcyBkaXNwb3NzZW4gZGUgdmFsb3JzIGRlIHNhbGFyaSBtw61uaW0gaSBtw6F4aW0uIEFxdWVzdHMgZW5zIHBvZHJpZW4gc2VyIHN1ZmljaWVudCBwZXIgYWwgbm9zdHJlcyBlc3R1ZGksIHNlbXByZSBpIHF1YW4gdGluZ3VlbSBzdWZpY2llbnQgY2Fzb3MgZCdlc3R1ZGkgcGVyIGEgbGVzIGRpZmVyZW50ZXMgcmVnaW9ucy4KCkxsYXZvcnMsIGVucyBxdWVkYXJlbSBhbWIgbGVzIGRhZGVzIHF1ZSB0ZW5lbiB1biBzYWxhcmkgbcOtbmltIGkgZGVzY2FydGFyZW0gbGEgcmVzdGEuCgpgYGB7cn0Kb2ZmZXJzX3NwIDwtIHN1YnNldChvZmZlcnMsICFpcy5uYShvZmZlcnMkc2FsYXJpb01pbikpCmBgYAoKYGBge3J9CnNhcHBseShvZmZlcnNfc3AsIGZ1bmN0aW9uKHgpKHN1bShpcy5uYSh4KSkpKQpgYGAKCkNvbSBxdWUgaGkgaGEgbWVueXMgZGFkZXMgYW1iIHNhbGFyaSBtw6F4aW0sIHBvZHJpZW0gc2VndWlyIGFsZ3VuYSBkZSBsZXMgc2Vnw7xlbnRzIGVzdHJhdMOoZ2llczoKLSBEZXNjYXJ0YXIgbGVzIGRhZGVzIHNlbnNlIHNhbGFyaSBtYXhpbS4gQWl4w7IgcmVkdWlyw61hIG1vbHQgZWwgbm9tYnJlIGRlIGRhZGVzLCBwZXLDsiBlbmNhcmEgdGluZHJpZW0gcHJvdSBwZXIgYWwgZXN0dWRpIHF1ZSB2b2xlbSByZWFsaXR6YXIuCi0gSW1wdXRhciBkYWRlcyB1dGlsaXR6YW50IEtOTi4gQW1iIGFxdWVzdGEgZXN0cmF0w6hnaWEgcG9kcmllbSBvYnRlbmlyIGVscyB2YWxvcnMgZW4gZnVuY2nDsyBkZSBsZXMgZGFkZXMgdmVpbmVzLgotIEltcHV0YXIgZWxzIHZhbG9ycyBlbiBmdW5jacOzIGRlIGxhIG1pdGphbmEgcG9ibGFjaW9uYWwgZGUgbGEgbW9zdHJhLgoKRW4gYXF1ZXN0YSBvY2FzacOzIHV0aWxpdHphcmVtIGwnYWxnb3JpdG1lIEtOTi4KCmBgYHtyfQpvZmZlcnNfc3Akc2FsYXJpb01heCA8LSBrTk4ob2ZmZXJzX3NwKSRzYWxhcmlvTWF4CnNhcHBseShvZmZlcnNfc3AsIGZ1bmN0aW9uKHgpKHN1bShpcy5uYSh4KSkpKQpgYGAKCj5OT1RFOiB0aGUgZm9sbG93aW5nIGNodW5rIHNlZW1zIHRvIGJlIGEgbWlzdGFrZSBpbiBleGVjdXRpbmcgdGhlIHByZXZpb3VzIGNvZGUgc2V2ZXJhbCB0aW1lcy4KVmVpZW0gcXVlIHRlbmltIG1vbHRlcyBjaXV0YXRzIGFtYiBOQS4gU2VndWlyZW0gbGEgbWF0ZWl4YSBlc3RyYXRlZ2lhIHF1ZSBhYmFucyBpIGNvbSBxdWUgc2FiZW0gcXVlIGxhIG9mZXJ0YSBoYSBkJ2VzdGFyIHViaWNhZGEgZW4gYWxndW5hIGNpdXRhdCAoZW5jYXJhIHF1ZSBzaWd1aSBvZmVydGEgb25saW5lKSwgc3Vic3RpdHVpcmVtIGVscyBOQSBwZXIgdmFsb3JzIGJ1aXRzICIiLgoKYGBge3IsIGV2YWw9RkFMU0V9Cm9mZmVyc19zcCRjaXVkYWRGW2lzLm5hKG9mZmVyc19zcCRjaXVkYWRGKV0gPC0gIiIKc2FwcGx5KG9mZmVyc19zcCwgZnVuY3Rpb24oeCkoc3VtKGlzLm5hKHgpKSkpCmBgYAoKQXJhIGVucyBxdWVkZW4gcGVyIHRyYWN0YXIgNCBjYXNvcyBkZSBjYXRlZ29yaWFGIGkgMiBkZSBzdWJjYXRlZ29yaWFTLgoKYGBge3J9CmthYmxlKHN1YnNldChvZmZlcnNfc3AsIGlzLm5hKG9mZmVyc19zcCRjYXRlZ29yaWFGKSkpCmBgYAoKTWlyYW50IGxhIHN1YmNhdGVnb3JpYSwgdmVpZW0gY2xhcmFtZW50IHF1ZSBsZXMgZHVlcyDDumx0aW1lcyBwZXJ0YW55ZW4gYSBsYSBjYXRlZ29yaWE6IElORk9STcOBVElDQS9URUxFQ09NVU5JQ0FDSU9ORVMsIHBlcsOyIG1hbGF1cmFkYW1lbnQsIGxlcyBkdWVzIHByaW1lcmVzIG5vIHRlbmVuIHN1YmNhdGVnb3JpYS4gQWl4w60gZG9uY3MsIGRlc2NhcnRhcmVtIGxlcyBkdWVzIHByaW1lcmVzIGkgZW5zIHF1ZWRhcmVtIGFtYiBsZXMgZHVlcyDDumx0aW1lcywgaW50cm9kdWludCBlbCBub3UgdmFsb3IgYSBsYSBjYXRlZ29yaWEuCgpgYGB7cn0Kb2ZmZXJzX3NwIDwtIHN1YnNldChvZmZlcnNfc3AsICFpcy5uYShvZmZlcnNfc3Akc3ViY2F0ZWdvcmlhUykpCmBgYAoKYGBge3J9CnNhcHBseShvZmZlcnNfc3AsIGZ1bmN0aW9uKHgpKHN1bShpcy5uYSh4KSkpKQpgYGAKCkNvbSBxdWUgbm9tw6hzIGVucyBxdWVkZW4gZG9zIHZhbG9ycyBOQSBwZXIgc3Vic3RpdHVpciBpIHNvbiBlbHMgcXVlIGNvbmVpeGVtLCBwb2RlbSBmZXIgbGEgc2Vnw7xlbnQgb3BlcmFjacOzLgoKYGBge3J9Cm9mZmVyc19zcFtpcy5uYShvZmZlcnNfc3ApXSA8LSBjKCJJTkZPUk3DgVRJQ0EvVEVMRUNPTVVOSUNBQ0lPTkVTIikKYGBgCgpgYGB7cn0Kc2FwcGx5KG9mZmVyc19zcCwgZnVuY3Rpb24oeCkoc3VtKGlzLm5hKHgpKSkpCmBgYAoKQ29tcHJvdmVtIHF1ZSBqYSBubyB0ZW5pbSBjYXAgdmFsb3IgTkEuCgpgYGB7cn0Kc3VtbWFyeShvZmZlcnNfc3ApCmBgYAoKU2VndWlkYW1lbnQgcG9kcmllbSBjb21wcm92YXIgc2kgbGVzIG5vc3RyZXMgZGFkZXMgdGVuZW4gZWwgdGlwdXMgcXVlIGRlc2l0amVtLgoKYGBge3J9CnNhcHBseShvZmZlcnNfc3AsIGZ1bmN0aW9uKHgpKGNsYXNzKHgpKSkKYGBgCgpWZWllbSBxdWUgaGF1cmVtIGRlIHRyYWN0YXIgZWwgZm9ybWF0IGRlIGxhIGNhcmFjdGVyw61zdGljYSBmZWNoYUNyZWFjaW9uLiBFbiBhcXVlc3QgbW9tZW50LCB0ZW5pbSBsYSBkYXRhIGNvbSBhIHVuIHN0cmluZyBhbWIgZWwgZm9ybWF0OiBhbnlzLCBtZXMsIGRpYSwgaG9yYS4gRW4gZWwgbm9zdHJlIGNhcywgbm9tw6hzIGFtYiBsJ2FueSwgbWVzIGkgZGlhIGVuIHRpbmRyZW0gcHJvdS4gQSBtw6lzLCBoYXVyZW0gZGUgZG9uYXItbGkgZWwgdGlwdXMgZGUgZGF0ZSB0eXBlLgoKYGBge3J9Cm9mZmVyc19zcCRmZWNoYUNyZWFjaW9uIDwtIGFzLkRhdGUoZ3N1YigiVFxcZCo6XFxkKjpcXGQqWiIsICIiLCBvZmZlcnNfc3AkZmVjaGFDcmVhY2lvbikpCnNhcHBseShvZmZlcnNfc3AsIGZ1bmN0aW9uKHgpKGNsYXNzKHgpKSkKYGBgCgpgYGB7cn0Kc3VtbWFyeShvZmZlcnNfc3ApCmBgYAoKUGVyIMO6bHRpbSwgcG9kZW0gY2FudmlhciBlbCBub21icmUgZGUgbGVzIGNhcmFjdGVyw61zdGlxdWVzIHBlciBxdWUgdGluZ3VpbiB1bmEgbWljYSBtw6lzIGRlIHNlbnRpdCBpIGd1YXJkZW0gbGVzIGRhZGVzIGVuIHVuIG5vdSBmaXR4ZXIgY3N2LgoKYGBge3J9Cm5hbWVzKG9mZmVyc19zcCkKYGBgCgpgYGB7cn0KZmluYWxfbmFtZXMgPC0gYygiQ2F0ZWdvcmlhIiwgIkNpdWRhZCIsICJDb211bmlkYWQiLCAiRWR1Y2FjaW9uIiwgIkZlY2hhQ3JlYWNpb24iLCAiVGlwb0pvcm5hZGEiLCAiUHJvdmluY2lhIiwgIlNhbGFyaW9NYXgiLCAiU2FsYXJpb01pbiIsICJTdWJDYXRlZ29yaWEiKQpuYW1lcyhvZmZlcnNfc3ApIDwtIGZpbmFsX25hbWVzCmhlYWQob2ZmZXJzX3NwKQpgYGAKCkRlc3Byw6lzIGRlIGZlciBhcXVlc3RhIG5ldGVqYSwgZW5jYXJhIHBvZHJpZW0gY29tcHJvYmFyIHNpIGxlcyBub3N0cmVzIGRhZGVzIHNvbiBjb25zaXN0ZW50cy4gUGVyIGZlciBhaXjDsyBwb2RyaWVtIG1pcmFyIHNpIHRlbmltIG9mZXJ0ZXMgZW4gbGVzIHF1ZSBlbCBzYWxhcmkgbcOtbmltIHNpZ3VpIG1hbGxvciBxdWUgZWwgc2FsYXJpIG3DoHhpbSwgaSBkZSBzZXIgYWl4w60sIGVsaW1pbmFyLWxlcyBkZWwgbm9zdHJlIGRhdGFzZXQuCgpgYGB7cn0KbGVuZ3RoKHN1YnNldChvZmZlcnNfc3AsIFNhbGFyaW9NYXggPCBTYWxhcmlvTWluKSRTYWxhcmlvTWF4KQpgYGAKClZlaWVtIHF1ZSBlZmVjdGl2YW1lbnQsIHRlbmltIDIwMzUgb2ZlcnRlcyBhbWIgZGFkZXMgaW5jb25zaXN0ZW50cy4gUHJvY2VkaXJlbSBkb25jcyBhIGVsaW1pbmFyLWxlcy4KCmBgYHtyfQpvZmZlcnNfc3AgPC0gc3Vic2V0KG9mZmVyc19zcCwgU2FsYXJpb01heCA+PSBTYWxhcmlvTWluKQpgYGAKClBlciDDumx0aW0sIHBvZHJpZW0gZXhwb3J0YXIgZWwgbm9zdHJlIGNvbmp1bnQgZGUgZGFkZXMgbmV0ZWphdC4KCmBgYHtyfQp3cml0ZS5jc3Yob2ZmZXJzX3NwLCAiLi9kYXRhL3NwYW5pc2hfam9iX29mZmVyc19jbGVhbi5jc3YiKQpgYGAKCiMjIElkZW50aWZpY2FjacOzIGkgdHJhY3RhbWVudCBkZSB2YWxvcnMgZXh0cmVtcy4KCkRvbmVtIGFyYSB1biBjb3AgZCd1bGwgYSBsZXMgZGFkZXMgcGVyIHRhbCBkJ2lkZW50aWZpY2FyIHZhbG9ycyBleHRyZW1zLgoKYGBge3J9CmJveHBsb3Qob2ZmZXJzX3NwJFNhbGFyaW9NaW4pCmBgYAoKYGBge3J9CmJveHBsb3Qob2ZmZXJzX3NwJFNhbGFyaW9NYXgpCmBgYAoKYGBge3J9CmJveHBsb3Qob2ZmZXJzX3NwJFNhbGFyaW9NaW4gfiBvZmZlcnNfc3AkQ29tdW5pZGFkKQpgYGAKCmBgYHtyfQpib3hwbG90KG9mZmVyc19zcCRTYWxhcmlvTWF4IH4gb2ZmZXJzX3NwJENvbXVuaWRhZCkKYGBgCgpgYGB7cn0KYm94cGxvdC5zdGF0cyhvZmZlcnNfc3AkU2FsYXJpb01pbikkb3V0CmBgYAoKYGBge3J9CmJveHBsb3Quc3RhdHMob2ZmZXJzX3NwJFNhbGFyaW9NYXgpJG91dApgYGAKClZlaWVtIHF1ZSB0ZW5pbSB2YWxvcnMgZXh0cmVtcyB0YW50IGVuIGVscyBzYWxhcmlzIG3DoHhpbXMgY29tIGVuIGVscyBtw61uaW1zLiBFbiBlbCBjYXMgZGVscyBzYWxhcmlzIG3DrW5pbXMsIHNvbiB2YWxvciByYW9uYWJsZXMsIGkgY3JlYyBxdWUgZWxzIGhhdXJpZW0gZGUgZGVpeGFyIHRhbCBxdWFsIHNvbi4gRW4gY2FudmksIHRyb2JlbSBkb3MgdmFsb3JzIGV4dHJlbXMgbW9sdCBjdXJpb3NvcywgcXVlIHNlbWJsZW4gc2VyIGFsZ3VuYSBtZW5hIGRlIHZhbG9yIHByZWZpeGF0IHBlciBhIG5vIGRvbmFyIHVuIGzDrW1pdCBzdXBlcmlvci4gRW4gYXF1ZXN0IGNhcywgamEgcXVlIHNvbiBub23DqXMgZG9zIHZhbG9ycyBpIHRlbmltIHN1ZmljaWVudCBkYWRlcyBwZXIgYWwgbm9zdHJlIGVzdHVkaSwgY29uc2lkZXJvIHF1ZSBsbyBtaWxsb3Igc2Vyw61hIHRyZXVyZSBsZXMgZGFkZXMgY29ycmVzcG9uZW50cy4gQWl4w60gZG9uY3MsIGhvIGZhcmVtIGRlIGxhIHNlZ8O8ZW50IG1hbmVyYS4KCmBgYHtyfQprYWJsZShzdWJzZXQob2ZmZXJzX3NwLCBTYWxhcmlvTWF4ID09IDk5OTk5OTkpKQpgYGAKCmBgYHtyfQprYWJsZShzdWJzZXQob2ZmZXJzX3NwLCBTYWxhcmlvTWluID09IDApKQpgYGAKCmBgYHtyfQpvZmZlcnNfc3AgPC0gc3Vic2V0KG9mZmVyc19zcCwgIShTYWxhcmlvTWF4PT05OTk5OTk5KSkKYGBgCgpgYGB7cn0Kb2ZmZXJzX3NwJFNhbGFyaW9NaW4gPC0gYXMubnVtZXJpYyhvZmZlcnNfc3AkU2FsYXJpb01pbikKb2ZmZXJzX3NwJFNhbGFyaW9NYXggPC0gYXMubnVtZXJpYyhvZmZlcnNfc3AkU2FsYXJpb01heCkKYm94cGxvdChvZmZlcnNfc3AkU2FsYXJpb01heCB+IG9mZmVyc19zcCRDb211bmlkYWQpCmBgYAoKVmVpZW0gcXVlIGVscyB2YWxvcnMgZXh0cmVtcyBxdWUgdGVuaW0gYXJhIHNvbiBtw6lzIHJhb25hYmxlcywgaSBjb25zaWRlcm8gcXVlIGVscyBwb2RyaWVtIGRlaXhhciB0YWwgcXVhbC4KCmBgYHtyfQpib3hwbG90KG9mZmVyc19zcCRTYWxhcmlvTWF4IH4gb2ZmZXJzX3NwJENhdGVnb3JpYSkKYGBgCgojIDQuIEFuw6BsaXNpIGRlIGxlcyBkYWRlcwojIyBTZWxlY2Npw7MgZGVscyBncnVwcyBkZSBkYWRlcyBxdWUgZXMgdm9sZW4gYW5hbGl0emFyL2NvbXBhcmFyIChwbGFuaWZpY2FjacOzIGRlbHMgYW7DoGxpc2lzIGEgYXBsaWNhcikKCj5UT0RPOgpBZ3J1cGFyIHBvciBjb211bmlkYWQKCiMjIENvbXByb3ZhY2nDsyBkZSBsYSBub3JtYWxpdGF0IGkgaG9tb2dlbmXDr3RhdCBkZSBsYSB2YXJpw6BuY2lhLgoKQ29tZW7Dp2VtIG1pcmFudCBzaSBsZXMgdmFyaWFibGVzIHBlcnRhbnllbiBhIHVuYSBkaXN0cmlidWNpw7Mgbm9ybWFsLgoKYGBge3J9CnBfdmFsX3NhbF9taW4gPC0gc2hhcGlyby50ZXN0KHN1YnNldChvZmZlcnNfc3AsIENvbXVuaWRhZCA9PSBjKCJNQURSSUQiKSkkU2FsYXJpb01pbikkcC52YWx1ZQojcF92YWxfc2FsX21heCA8LSBzaGFwaXJvLnRlc3Qob2ZmZXJzX3NwJFNhbGFyaW9NYXgpJHAudmFsdWUKc3ByaW50ZigiUCB2YWx1ZSBwYXJhIFNhbGFyaW9NaW46ICVmIiwgcF92YWxfc2FsX21pbikKI3NwcmludGYoIlAgVmFsdWUgcGFyYSBTYWxhcmlvTWF4OiAlZCIsIHBfdmFsX3NhbF9tYXgpCmBgYAoKYGBge3J9Cmhpc3Qoc3Vic2V0KG9mZmVyc19zcCwgQ29tdW5pZGFkID09IGMoIk1BRFJJRCIpKSRTYWxhcmlvTWluKQpgYGAKCmBgYHtyfQpoaXN0KHN1YnNldChvZmZlcnNfc3AsIENvbXVuaWRhZCA9PSBjKCJDQVRBTFXDkUEiKSkkU2FsYXJpb01pbikKYGBgCgojIyBBcGxpY2FjacOzIGRlIHByb3ZlcyBlc3RhZMOtc3RpcXVlcyBwZXIgY29tcGFyYXIgZWxzIGdydXBzIGRlIGRhZGVzLiBFbiBmdW5jacOzIGRlIGxlcyBkYWRlcyBkZSBsJ29iamVjdGl1IGRlIGwnZXN0dWRpLCBhcGxpY2FyIHByb3ZlcyBkZSBjb250cmFzdCBkJ2hpcMOydGVzaSwgY29ycmVsYWNpb25zLCByZWdyZXNzaW9ucywgZXRjLgoKYGBge3J9CmNvcl9tYXRyaXggPC0gY29yKG9mZmVyc19zcCRTYWxhcmlvTWluLCBvZmZlcnNfc3AkU2FsYXJpb01heCkKcm91bmQoY29yX21hdHJpeCwgMikKYGBgCgpFbiBhcXVlc3QgcHVudCwgZW5zIGFkb25lbSBxdWUgaGkgaGEgdW4gdGlwdXMgZGUgcmVnaXN0cmVzIGVuIGVscyBxdWFscyB0ZW5pbSAwIGEgc2FsYXJpIG1pbmltIGkgbcOheGltLCBsbyBxdWUgdm9sIGRpciBxdWUgYXF1ZXN0ZXMgb2ZlcnRlcyBubyBoYW4gaW50cm9kdWl0IHVuIHZhbG9yIHJlYWwgZW4gcXVhbnQgYWxzIHNhbGFyaXMsIG8gYsOpIHNvbiBvZmVydGVzIGRlIHByw6BjdGlxdWVzIG5vIHJlbXVuZXJhZGVzLiBOaW5ndW5hIGQnYXF1ZXN0ZXMgb3BjaW9ucyBsZXMgdm9sZW0gY29udGVtcGxhciBlbiBlbCBub3N0cmUgZXN0dWRpLCBhaXjDrSBxdWUgY29tIHRlbmltIGRhZGVzIHN1ZmljaWVudHMsIHBvZGVtIHByZXNjaW5kaXIgZCdhcXVlc3Rlcy4KCmBgYHtyfQpvZmZlcnNfc3AgPC0gc3Vic2V0KG9mZmVyc19zcCwgIShTYWxhcmlvTWluID09IDAgJiBTYWxhcmlvTWF4ID09IDApKQpgYGAKCj4gVE9ETzogRW4gY29tcHRlcyBkJ3V0aWxpdHphciBLTk4gdXRpbGl0emFyIGxlcyBtaXRqYW5lcyBwb2JsYWNpb25hbHMgcGVyIGNhdGVnb3JpYQo+IFRPRE86IEhpIGhhIHZhbG9ycyBkZSBzYWxhcmkgTWluIGkgTWF4IHF1ZSBzJ2hhbiBkJ2ludGVyY2Fudmlhci4KCgojIDUuIFJlcHJlc2VudGFjacOzIGRlbHMgcmVzdWx0YXRzIGEgcGFydGlyIGRlIHRhdWxlcyBpIGdyw6BmaXF1ZXMuCgojIDYuIFJlc29sdWNpw7MgZGVsIHByb2JsZW1hLiBBIHBhcnRpciBkZWxzIHJlc3VsdGF0cyBvYnRpbmd1dHMsIHF1aW5lcyBzw7NuIGxlcyBjb25jbHVzaW9ucz8gRWxzIHJlc3VsdGF0cyBwZXJtZXRlbiByZXNwb25kcmUgYWwgcHJvYmxlbWE/CgojIDcuIENvZGk6IENhbCBhZGp1bnRhciBlbCBjb2RpCgo=